package top.jiangqiang.qianyi.generator;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import lombok.Builder;
import lombok.Data;
import lombok.Value;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.reflect.ClassUtil;
import org.dromara.hutool.core.reflect.FieldUtil;
import org.dromara.hutool.core.reflect.ModifierUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.jetbrains.annotations.NotNull;
import top.jiangqiang.qianyi.generator.freemarker.EnhanceFreemarkerTemplateEngine;
import top.jiangqiang.qianyi.generator.model.*;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 通用代码生成器封装
 * @Date 2024/1/11 14:58
 * @Author Jiangqiang
 */
@Builder
@Data
@Value
public class CodeGenerator {
    /**
     * 作者名
     */
    String author;
    @Builder.Default
    EntityFileConfig entityFileConfig = EntityFileConfig.builder().generateFile(false).build();
    @Builder.Default
    MapperFileConfig mapperFileConfig = MapperFileConfig.builder().generateMapperFile(false).generateXmlFile(false).build();
    @Builder.Default
    DbServiceFileConfig dbServiceFileConfig = DbServiceFileConfig.builder().generateServiceFile(false).generateServiceImplFile(false).build();
    @Builder.Default
    ControllerFileConfig controllerFileConfig = ControllerFileConfig.builder().generateFile(false).build();
    /**
     * 要生成代码的表，如果为空，则生成所有表
     * includeTables和excludeTables只能生效一个
     */
    @Builder.Default
    List<String> includeTables = ListUtil.empty();
    /**
     * 不生成代码的表
     * includeTables和excludeTables只能生效一个
     */
    @Builder.Default
    List<String> excludeTables = ListUtil.empty();
    @Builder.Default
    List<String> tablePrefix = ListUtil.empty();
    @Builder.Default
    List<CustomFileConfig> customFileConfigs = ListUtil.empty();

    /**
     * 获取指定包下实体类可能对应的表名
     *
     * @param excludePackageStr 指定包名
     * @param tablePrefix       表前缀
     * @return
     */
    public static List<String> listTableNames(String excludePackageStr, String... tablePrefix) {
        List<String> tableNames = new ArrayList<>();
        if (StrUtil.isNotBlank(excludePackageStr)) {
            Set<Class<?>> classes = ClassUtil.scanPackage(excludePackageStr);
            classes.forEach(aClass -> {
                TableName annotation = aClass.getAnnotation(TableName.class);
                if (annotation != null && StrUtil.isNotBlank(annotation.value())) {
                    tableNames.add(annotation.value());
                    return;
                }
                String tableName = StrUtil.toUnderlineCase(aClass.getSimpleName());
                tableNames.add(tableName);
                if (ArrayUtil.isNotEmpty(tablePrefix)) {
                    for (String prefix : tablePrefix) {
                        tableNames.add(StrUtil.addPrefixIfNot(tableName,prefix));
                    }
                }
            });
        }
        return tableNames;
    }

    /**
     * 根据当前代码生成的绝对路径获取包名
     *
     * @param path 例如：D:\Documents\IdeaProjects\basic-common\basic-common-mp\src\main\java\cn\chinatelecom\cq\ctis\common\mp
     * @return
     */
    private static String getPackageStr(String path) {
        if (StrUtil.isBlank(path)) {
            return "";
        }
        //替换反斜杠为斜杠
        path = StrUtil.replace(path, "\\", "/");
        if (!path.contains("/java/")) {
            return "";
        }
        path = StrUtil.subAfter(path, "/java/", true);
        return StrUtil.removeSuffix(StrUtil.replace(path, "/", "."), ".");
    }

    private static String toImplPath(String path) {
        if (StrUtil.isBlank(path)) {
            path = "";
        }
        //替换反斜杠为斜杠
        path = StrUtil.replace(path, "\\", "/");
        //移除末尾的斜杠
        path = StrUtil.removeSuffix(path, "/");
        return StrUtil.replace(path, "/", FileUtil.FILE_SEPARATOR) + FileUtil.FILE_SEPARATOR + "impl";
    }

    public void globalConfig(GlobalConfig.Builder builder) {
        if (StrUtil.isNotBlank(author)) {
            // 设置作者
            builder.author(author);
        }
        builder
//                .enableSwagger() // 开启 swagger 模式
                .enableSpringdoc() //使用springdoc文档
                .disableOpenDir() //生成之后打开生成目录
                .dateType(DateType.TIME_PACK);
    }

    public void strategyConfig(StrategyConfig.Builder builder) {
        if (CollUtil.isNotEmpty(excludeTables)) {
            // 设置需要排除的表名
            builder.addExclude(excludeTables);
        } else {
            // 设置需要生成的表名
            builder.addInclude(includeTables);
        }
        builder.addTablePrefix(tablePrefix);
        if (!entityFileConfig.isGenerateFile()) {
            builder.entityBuilder().disable();
        }
        if (!mapperFileConfig.isGenerateMapperFile()) {
            builder.mapperBuilder().disableMapper();
        }
        if (!mapperFileConfig.isGenerateXmlFile()) {
            builder.mapperBuilder().disableMapperXml();
        }
        if (!dbServiceFileConfig.isGenerateServiceFile()) {
            builder.serviceBuilder().disableService();
        }
        if (!dbServiceFileConfig.isGenerateServiceImplFile()) {
            builder.serviceBuilder().disableServiceImpl();
        }
        if (!controllerFileConfig.isGenerateFile()) {
            builder.controllerBuilder().disable();
        }
        if (!entityFileConfig.isFileOverride()) {
            builder.entityBuilder().enableFileOverride();
        }
        if (!mapperFileConfig.isFileOverride()) {
            builder.mapperBuilder().enableFileOverride();
        }
        if (!dbServiceFileConfig.isFileOverride()) {
            builder.serviceBuilder().enableFileOverride();
        }
        if (!controllerFileConfig.isFileOverride()) {
            builder.controllerBuilder().enableFileOverride();
        }

        builder.entityBuilder()
                .formatFileName(entityFileConfig.getFormatName())
                .javaTemplate(entityFileConfig.getTemplatePath())
                .addSuperEntityColumns(getColumns(entityFileConfig.getSuperClass()))
                .idType(IdType.ASSIGN_ID)//主键策略
//                .enableChainModel()
                .enableLombok();
        if (entityFileConfig.getSuperClass() != null) {
            builder.entityBuilder()
                    .superClass(entityFileConfig.getSuperClass());
        }
        if (entityFileConfig.getImportPackageFunction() != null) {
            builder.entityBuilder().importPackageFunction(entityFileConfig.getImportPackageFunction());
        }
        if (entityFileConfig.getTableFieldAnnotationHandler() != null) {
            builder.entityBuilder().tableFieldAnnotationHandler(entityFileConfig.getTableFieldAnnotationHandler());
        }
        builder.mapperBuilder()
                .formatMapperFileName(mapperFileConfig.getMapperFormatName())
                .mapperTemplate(mapperFileConfig.getMapperTemplatePath())
                .enableBaseColumnList()
                .enableBaseResultMap()
                .formatXmlFileName(mapperFileConfig.getXmlFormatName())
                .mapperXmlTemplate(mapperFileConfig.getXmlTemplatePath());
        if (mapperFileConfig.getSuperClass() != null) {
            builder.mapperBuilder()
                    .superClass(mapperFileConfig.getSuperClass());
        }

        builder.serviceBuilder()//service策略
                .serviceTemplate(dbServiceFileConfig.getServiceTemplatePath())
                .formatServiceFileName(dbServiceFileConfig.getServiceFormatName())
                .serviceImplTemplate(dbServiceFileConfig.getServiceImplTemplatePath())
                .superServiceImplClass(dbServiceFileConfig.getServiceImplSuperClass())
                .formatServiceImplFileName(dbServiceFileConfig.getServiceImplFormatName());
        if (dbServiceFileConfig.getServiceSuperClass() != null) {
            builder.serviceBuilder()
                    .superServiceClass(dbServiceFileConfig.getServiceSuperClass());
        }

        builder.controllerBuilder()//controller策略
                .formatFileName(controllerFileConfig.getFormatName())
                .template(controllerFileConfig.getTemplatePath())
                .enableRestStyle();
        if (controllerFileConfig.getSuperClass() != null) {
            builder.controllerBuilder()
                    .superClass(controllerFileConfig.getSuperClass());
        }
    }

    private String @NotNull [] getColumns(Class<?> superClass) {
        if (superClass == null) {
            return new String[0];
        }
        //忽略父类中的字段在子类中生成
        return Arrays.stream(FieldUtil.getFields(superClass)).filter(field -> !ModifierUtil.isStatic(field)).map(Field::getName).map(StrUtil::toUnderlineCase).toArray(String[]::new);
    }

    public void packageConfig(PackageConfig.Builder builder) {
        builder.parent("");
        Map<OutputFile, String> map = new HashMap<>();
        map.put(OutputFile.entity, entityFileConfig.getFilePath());
        map.put(OutputFile.service, dbServiceFileConfig.getServiceFilePath());
        String dbServiceImplPath;
        if (StrUtil.isBlank(dbServiceFileConfig.getServiceImplFilePath())) {
            dbServiceImplPath = toImplPath(dbServiceFileConfig.getServiceFilePath());
        } else {
            dbServiceImplPath = dbServiceFileConfig.getServiceImplFilePath();
        }
        map.put(OutputFile.serviceImpl, dbServiceImplPath);
        map.put(OutputFile.mapper, mapperFileConfig.getMapperFilePath());
        map.put(OutputFile.xml, mapperFileConfig.getXmlFilePath());
        map.put(OutputFile.controller, controllerFileConfig.getFilePath());
        builder.parent("").pathInfo(map).entity(getPackageStr(entityFileConfig.getFilePath())).service(getPackageStr(dbServiceFileConfig.getServiceFilePath())).serviceImpl(getPackageStr(dbServiceImplPath)).mapper(getPackageStr(mapperFileConfig.getMapperFilePath())).controller(getPackageStr(controllerFileConfig.getFilePath()));
    }

    public void injectionConfig(InjectionConfig.Builder builder) {
        List<CustomFileConfig> list = customFileConfigs.stream()
                .filter(customFileConfig -> customFileConfig.isGenerateFile())
                .toList();
        List<CustomFile> customFiles = new ArrayList<>();
        for (int index = 1; index < list.size() + 1; index++) {
            CustomFileConfig customFileConfig = list.get(index - 1);
            String namespace = customFileConfig.getNamespace();
            if (StrUtil.isBlank(namespace)) {
                namespace = "customFile" + index;
            }
            CustomFile customFile = EnhanceCustomFile.builder()
                    .fileName(namespace)
                    .formatNameFunction(customFileConfig.getFormatNameFunction())
                    .templatePath(customFileConfig.getTemplatePath())
                    .packageName(getPackageStr(customFileConfig.getFilePath()))
                    .filePath(customFileConfig.getFilePath())
                    .fileOverride(customFileConfig.isFileOverride())
                    .superClass(customFileConfig.getSuperClass())
                    .extParamsFunction(customFileConfig.getExtParamsFunction())
                    .build();
            customFiles.add(customFile);
        }
        if (CollUtil.isNotEmpty(customFiles)) {
            builder.customFile(customFiles);
        }
    }

    public void execute(String url, String username, String password) {
        FastAutoGenerator.create(url, username, password)
                /*全局配置*/.globalConfig(this::globalConfig)
                /*策略配置，包括模板路径配置*/.strategyConfig(this::strategyConfig)
                /*包配置*/.packageConfig(this::packageConfig)
                /*注入配置*/.injectionConfig(this::injectionConfig)
                /*使用Freemarker引擎模板，默认的是Velocity引擎模板*/.templateEngine(new EnhanceFreemarkerTemplateEngine()).execute();
    }

}
